/*
 * readBuffer.c
 *
 *  Created on: 2012. 4. 16.
 *      Author: jphong
 */
#include	<stdio.h>
#include	<stdlib.h>
#include	<unistd.h>
#include	<ctype.h>
#include	<errno.h>
#include	<sys/types.h>
#include	<string.h>
#include	<sys/uio.h>
#include	"readBuffer.h"

/*
 * Initialize ReadBuffer
 */
void initRbuf(ReadBuffer *rbuf) {
	rbuf->count = 0;
	rbuf->rear = rbuf->front = 0;
}

/* Create ReadBuffer with buffer length with bufsize
 * 		rbuf->buf:	start of buffer
 * 		rbuf->bufsize:	buffer size
 * 		rbuf->rear: index of buf to get string from the buffer, i.e. buf[rear]
 * 		rbug->front: index of buf to put on the buffer (first empty space)
 * 		rbuf->count: # of characters fill in buffer
 * 	Returns:
 * 		pointer to ReadBuffer if successful allocation
 * 		NULL if malloc() failed
 */
ReadBuffer *
createRbuf(ssize_t bufsize) {
	ReadBuffer *rbuf;

	if (((rbuf = (ReadBuffer *) malloc(sizeof(ReadBuffer))) != NULL) && ((rbuf->buf = (char *) malloc((size_t) bufsize)) != NULL)) {
		;
	} else
		return (NULL); // Not enough memory

	// Initialize the readBuffer node
	initRbuf(rbuf);
	rbuf->bufsize = bufsize;

	return (rbuf); // returns pointer to the ReadBuffer
}

/*
 * Destroy ReadBuffer
 */
void destroyRbuf(ReadBuffer *rbuf) {
	if (rbuf != NULL) {
		free(rbuf->buf);
		free(rbuf);
	}
}

/*
 * Read from fd and append it to readBuffer
 * Returns:	same as return value of read()
 * 			# of bytes read	if positive
 * 			0				if FIN receviced
 * 			-1				on read() error
 */
ssize_t readBuf(ReadBuffer *rbuf, int fd) {
	ssize_t n;

	struct iovec iov[2];
	int empty, iov_cnt;

	empty = rbuf->bufsize - rbuf->count;
	iov[0].iov_base = rbuf->buf + rbuf->front;
	if ((rbuf->front + empty) > rbuf->bufsize) { // empty space wrap around
		iov[0].iov_len = rbuf->bufsize - rbuf->front;
		iov[1].iov_base = rbuf->buf;
		iov[1].iov_len = empty - iov[0].iov_len;
		iov_cnt = 2;
	} else {
		iov[0].iov_len = empty;
		iov_cnt = 1;
	}

	again: if ((n = readv(fd, iov, iov_cnt)) < 0) {
		if (errno == EINTR)
			goto again;
		return (-1);
	} else if (n == 0) {
		return (0);
	}
	(rbuf->count) += n;
	(rbuf->front) = (rbuf->front + n) % rbuf->bufsize;

	return (n);
}

/*
 * Get a character from Rbuf
 * 	Returns:	0 if no more character
 */
unsigned char getChar(ReadBuffer *rbuf) {
	char c;

	if (rbuf->count <= 0) {
		return ('\0');
	}
	c = (rbuf->buf)[rbuf->rear];
	rbuf->rear = (rbuf->rear + 1) % rbuf->bufsize;
	(rbuf->count)--;
	return (c);
}

/*
 * Put a character to Rbuf
 *  Returns:	0 if no more space for putting a character
 */
unsigned char ungetChar(ReadBuffer *rbuf, unsigned char c) {
	if (rbuf->count == rbuf->bufsize) {
		return ('\0');
	}
	rbuf->rear = (rbuf->rear - 1) % rbuf->bufsize;
	(rbuf->buf)[rbuf->rear] = c;
	(rbuf->count)++;
	return (c);
}

/*
 * Get string terminated with 'suffix' string from ReadBuffer
 * 	(Reentrant and nonblocking function)
 * 	Returns:	copied string length	if found (string copied  into 'str')
 * 				 0		if ReadBuffer is empty
 * 				-1		if suffix not found
 */
ssize_t getSuffix(ReadBuffer *rbuf, char *str, ssize_t strsize, char *suffix) {
	int suffix_len;
	int count, rear, i;
	char *buf, *s;

	if (rbuf->count == 0)
		return (0);

	suffix_len = strlen(suffix);
	count = rbuf->count;
	rear = rbuf->rear;
	buf = rbuf->buf;

	for (s = str; (count >= suffix_len) && (s + suffix_len <= str + strsize); *s++ = buf[rear], count--, rear = (rear + 1) % rbuf->bufsize) {
		// compare to suffix
		for (i = 0; i < suffix_len; i++) {
			if (buf[(rear + i) % rbuf->bufsize] != suffix[i]) // Not equal
				break;
		}
		if (i == suffix_len) { // Found
			// Found
			for (i = 0; i < suffix_len; i++) { // Copy suffix
				*s++ = suffix[i];
			}
			*s = '\0';

			// advance ReadBuffer
			rbuf->count = count - suffix_len;
			rbuf->rear = (rear + suffix_len) % rbuf->bufsize;
			return (strlen(str));
		}
	}
	return (-1); // Not found. Try later
}

/* Get string of length 'len' from ReadBuffer
 * 	Returns:	copied string length	(string copied  into 'str')
 * 				-1		if not enough ReadBuffer
 */
ssize_t getString(ReadBuffer *rbuf, char *str, ssize_t len) {
	ssize_t len1;

	if (rbuf->count == 0)
		return (0); // ReadBuffer empty
	else if (len > rbuf->count)
		return (-1); // not enough

	if ((len1 = rbuf->rear + len - rbuf->bufsize) > 0) {
		memcpy(str, rbuf->buf + rbuf->rear, len - len1);
		memcpy(str + len - len1, rbuf->buf, len1);
	} else
		memcpy(str, rbuf->buf + rbuf->rear, len);
	*(str + len) = '\0'; // NULL-terminated
	rbuf->count -= len;
	rbuf->rear = (rbuf->rear + len) % rbuf->bufsize;

	return (len);
}

/*
 * Get a line terminated with <LF>, <CRLF>, <CRLF><CRLF> from ReadBuffer
 * 	(Reentrant and nonblocking function)
 * 	Returns:	copied string length	if found (string copied  into 'str')
 * 				 0		if ReadBuffer is empty
 * 				-1		if suffix not found
 */
ssize_t getLine(ReadBuffer *rbuf, char *buf, ssize_t bufsize) {
	return (getSuffix(rbuf, buf, bufsize, "\n"));
}

ssize_t getCRLF(ReadBuffer *rbuf, char *buf, ssize_t bufsize) {
	return (getSuffix(rbuf, buf, bufsize, "\r\n"));
}

ssize_t getMessage(ReadBuffer *rbuf, char *buf, ssize_t bufsize) {
	return (getSuffix(rbuf, buf, bufsize, "\r\n\r\n"));
}
